// CTask.cpp: implementation of the CTask class.
//


#include "stdafx.h"
#include "CTask.h"
#include <afxconv.h>
#include <comdef.h>

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif

#pragma comment(lib, "mstask.lib")
#pragma comment(lib, "taskschd.lib")
#pragma comment(lib, "comsupp.lib")

#define DDS_CST_EVERYMONTH \
                        TASK_JANUARY  | TASK_FEBRUARY | TASK_MARCH     | \
                        TASK_APRIL    | TASK_MAY      | TASK_JUNE      | \
                        TASK_JULY     | TASK_AUGUST   | TASK_SEPTEMBER | \
                        TASK_OCTOBER  | TASK_NOVEMBER | TASK_DECEMBER  

CTask::CTask()
{
    m_bNT = ( 0 == ( GetVersion() & 0x80000000 ) );
    Reset();
}

CTask::~CTask()
{
}

BOOL CTask::GetVersionTS()
{
	DWORD dwVersion = 0; 
	DWORD dwMajorVersion = 0;

	dwVersion = GetVersion();
	dwMajorVersion = (DWORD) ( LOBYTE( LOWORD( dwVersion ) ) );
	
	if ( dwMajorVersion >= 6 ) {
		return TRUE;
	} else {
		return FALSE;
	}
}

BOOL CTask::GetStartDateTime ( CTime& time ) const
{
	BOOL bRet = FALSE;

    if ( 0 != m_timeStart.wYear ) {
		time = m_timeStart;
		bRet = TRUE;
	} else {
		TRACE0("CTask::GetStartDateTime() - no start time set.\n");
	}
	
	return bRet;
}

void CTask::SetStartDateTime ( const CTime& time )
{
    time.GetAsSystemTime ( m_timeStart );

    m_timeStart.wSecond = 0;
    m_timeStart.wMilliseconds = 0;
}

void CTask::SetStartDateTime ( const SYSTEMTIME& time )
{
    m_timeStart = time;

    m_timeStart.wSecond = 0;
    m_timeStart.wMilliseconds = 0;
}

BOOL CTask::GetEndDate ( CTime& time ) const
{
	BOOL bRet = FALSE;

    if ( 0 != m_timeEnd.wYear ) {
		time = m_timeEnd;
		bRet = TRUE;
	} else {
		TRACE0("CTask::GetEndDateTime() - no end time set.\n");
	}
	
	return bRet;
}

void CTask::SetEndDate ( const CTime& time )
{
    ZeroMemory ( &m_timeEnd, sizeof(SYSTEMTIME) );

    m_timeEnd.wYear  = time.GetYear();
    m_timeEnd.wMonth = time.GetMonth();
    m_timeEnd.wDay   = time.GetDay();
}

void CTask::SetEndDate ( const SYSTEMTIME& time )
{
    ZeroMemory ( &m_timeEnd, sizeof(SYSTEMTIME) );

    m_timeEnd.wYear  = time.wYear;
    m_timeEnd.wMonth = time.wMonth;
    m_timeEnd.wDay   = time.wDay;
}

CTask::ETaskFrequency CTask::GetFrequency() const
{
    ASSERT ( m_eFreq >= freqFirst  &&  m_eFreq <= freqLast );

    return m_eFreq;
}

void CTask::SetFrequency  ( CTask::ETaskFrequency freq )
{
    ASSERT ( freq >= freqFirst  &&  freq <= freqLast );

    m_eFreq = freq;
}

CString CTask::GetProgram() const
{
    return m_sProgramPath;
}

void CTask::SetProgram ( LPCTSTR szProgram )
{
    ASSERT ( AfxIsValidString ( szProgram ));

#ifdef _DEBUG
    if ( 0xFFFFFFFF == ::GetFileAttributes ( szProgram ) )
        {
        TRACE1("CTask::SetProgram() - warning, program <%s> not found.\n",
               szProgram );
        }
#endif

    m_sProgramPath = szProgram;
}

CString CTask::GetParameters() const
{
    return m_sParameters;
}

void CTask::SetParameters ( LPCTSTR szParameters )
{
    ASSERT ( AfxIsValidString ( szParameters ));
    m_sParameters = szParameters;
}

CString CTask::GetStartingDir() const
{
    return m_sStartingDir;
}

void CTask::SetStartingDir ( LPCTSTR szStartingDir )
{
    ASSERT ( AfxIsValidString ( szStartingDir ));
    m_sStartingDir = szStartingDir;
}

CString CTask::GetAccountName() const
{
    return m_sAccount;
}

void CTask::SetAccountName ( LPCTSTR szAccount )
{
    ASSERT ( AfxIsValidString ( szAccount ));

    m_sAccount = szAccount;
}

void CTask::SetPassword ( LPCTSTR szPassword )
{
    //ASSERT ( AfxIsValidString ( szPassword ));
    m_sPassword = szPassword;
}

CString CTask::GetComment() const
{
    return m_sComment;
}

void CTask::SetComment ( LPCTSTR szComment )
{
    ASSERT ( AfxIsValidString ( szComment ));
    m_sComment = szComment;
}

void CTask::Reset()
{
    ZeroMemory ( &m_timeStart, sizeof(SYSTEMTIME) );
    ZeroMemory ( &m_timeEnd, sizeof(SYSTEMTIME) );

    m_eFreq = freqUnset;

    m_sProgramPath.Empty();
    m_sParameters.Empty();
    m_sStartingDir.Empty();
    m_sComment.Empty();
}

HRESULT CTask::SaveTask ( LPCTSTR szTaskName, BOOL bFailIfExists ) const
{
	if ( GetVersionTS() == TRUE ) {
		return SaveTaskTS2( szTaskName, bFailIfExists );
	} else {
		return SaveTaskTS1( szTaskName, bFailIfExists );
	}
}

HRESULT CTask::SaveTaskTS1 ( LPCTSTR szTaskName, BOOL bFailIfExists ) const
{
	HRESULT         hr;
	ITaskScheduler* pISched    = NULL;
	IUnknown*       pIUnk      = NULL;
	IPersistFile*   pIFile     = NULL;
	ITask*          pITask     = NULL;
	ITaskTrigger*   pITaskTrig = NULL;
	TASK_TRIGGER    rTrigger;
	DWORD           dwTaskFlags;
	WORD            wTrigNumber;
	
	USES_CONVERSION;
	
	ASSERT ( AfxIsValidString ( szTaskName ));

    if ( 0 == m_timeStart.wYear || NULL == szTaskName || 0 == lstrlen ( szTaskName ) || m_sProgramPath.GetLength() < 1 ) {
		return E_FAIL;
	}
	
	hr = ::CoCreateInstance (
		CLSID_CTaskScheduler,
		NULL,
		CLSCTX_INPROC_SERVER,
		IID_ITaskScheduler,
		(void **) &pISched
	);
	
	if ( FAILED(hr) ) {
#ifdef _DEBUG
        TRACE1("CTask::SaveTask() - failed to create a task scheduler interface. Return = 0x%08X\n",
               (DWORD) hr );

        if ( REGDB_E_CLASSNOTREG == hr )
            {
            TRACE0("    The error was REGDB_E_CLASSNOTREG, meaning you don't have the scheduler installed.\n"
                _T("    If you are running 95 or NT 4 with IE 4, you must install the task scheduler from the\n")
                _T("    IE components install page on MS's web site or the IE CD.\n")
                _T("    If you're on 98, NT 5, or 95/NT 4 with IE 5, then something's wrong with your install\n")
                _T("    because the scheduler should always be present.\n")
                _T("    Note that this class does *NOT* work with the \"AT\" service, which is the default\n")
                _T("    scheduler on NT 4 and earlier.\n") );
            }
#endif  // _DEBUG
		return hr;
	}
	
	__try {
		hr = pISched->Activate ( T2COLE ( szTaskName ), IID_ITask, &pIUnk );
		
		if ( SUCCEEDED(hr) ) {
			pIUnk->Release();
			pIUnk = NULL;
			
			if ( bFailIfExists ) {
				TRACE0("CTask::SaveTask() - A task with the given name already exists; failing.\n");
				return HRESULT_FROM_WIN32 ( ERROR_FILE_EXISTS );
			} else {
				TRACE0("CTask::SaveTask() - A task with the given name already exists; deleting it.\n");
				hr = CTask::DeleteTask ( szTaskName );
				
				if ( FAILED(hr) ) {
					TRACE1("CTask::SaveTask() - couldn't delete existing task! Bailing out. Return = 0x%08X\n", (DWORD) hr );
					return hr;
				}
			}
		}
		
		hr = pISched->NewWorkItem ( T2COLE ( szTaskName ), CLSID_CTask, IID_ITask, &pIUnk );
		
		if ( FAILED(hr) ) {
			TRACE1("CTask::SaveTask() - couldn't create a new work item. Return = 0x%08X\n", (DWORD) hr );
			return hr;
		}
		
		hr = pIUnk->QueryInterface ( IID_ITask, (void **) &pITask );
		
		if ( FAILED(hr) ) {
			TRACE1("CTask::SaveTask() - QI() on IUnknown failed to get an ITask. Return = 0x%08X\n", (DWORD) hr );
			return hr;
		}
		
		pIUnk->Release();
		pIUnk = NULL;
		
		hr = pITask->SetApplicationName ( T2COLE( (LPCTSTR) m_sProgramPath ));
		
		if ( FAILED(hr) ) {
			TRACE1("CTask::SaveTask() - failed to set application. Return = 0x%08X\n", (DWORD) hr );
			return hr;
		}
		
		if ( m_sParameters.GetLength() > 0 ) {
			hr = pITask->SetParameters ( T2COLE( (LPCTSTR) m_sParameters ));
			if ( FAILED(hr) ) {
				TRACE1("CTask::SaveTask() - failed to set parameters. Return = 0x%08X\n", (DWORD) hr );
				return hr;
			}
		} 
		
		if ( m_sStartingDir.GetLength() > 0 ) {
			hr = pITask->SetWorkingDirectory ( T2COLE( (LPCTSTR) m_sStartingDir ));
			if ( FAILED(hr) ) {
				TRACE1("CTask::SaveTask() - failed to set starting directory. Return = 0x%08X\n", (DWORD) hr );
				return hr;
			}
		}

		dwTaskFlags = 0;
		
		if ( !( GetVersion() & 0x80000000 ) ) {
			dwTaskFlags |= TASK_FLAG_INTERACTIVE;
		}

		if ( m_eFreq == freqAtLogon ) {
			dwTaskFlags |= TASK_FLAG_RUN_ONLY_IF_LOGGED_ON;
		}
		
		hr = pITask->SetFlags ( dwTaskFlags );
		
		if ( FAILED (hr) ) {
			TRACE1("CTask::SaveTask() - failed to set task flags. Return = 0x%08X\n", (DWORD) hr );
			return hr;
		}

		hr = pITask->SetPriority( IDLE_PRIORITY_CLASS );

		if ( FAILED (hr) ) {
			TRACE1("CTask::SaveTask() - failed to set task priority. Return = 0x%08X\n", (DWORD) hr );
			return hr;
		}

		hr = pITask->SetMaxRunTime( INFINITE );

		if ( FAILED (hr) ) {
			TRACE1("CTask::SaveTask() - failed to set task max run time. Return = 0x%08X\n", (DWORD) hr );
			return hr;
		}
		
		if ( m_bNT ) {
			if ( m_sAccount.Compare( _T("") ) == 0 ) {
				hr = pITask->SetAccountInformation ( L"", NULL );  // empty account implies SYSTEM account
			} else if ( dwTaskFlags & TASK_FLAG_RUN_ONLY_IF_LOGGED_ON ) {
				hr = pITask->SetAccountInformation ( T2COLE( (LPCTSTR) m_sAccount), NULL );
			} else {
				hr = pITask->SetAccountInformation ( T2COLE( (LPCTSTR) m_sAccount), T2COLE( (LPCTSTR) m_sPassword) );
			}
			
			if ( FAILED(hr) ) {
				TRACE1("CTask::SaveTask() - failed to set account info.  Return = 0x%08X\n", (DWORD) hr );
				return hr;
			}
		}
		
		if ( m_sComment.GetLength() > 0 ) {
			hr = pITask->SetComment ( T2COLE( (LPCTSTR) m_sComment ));
			if ( FAILED (hr) ) {
				TRACE1("CTask::SaveTask() - failed to set task comment. Return = 0x%08X\n", (DWORD) hr );
				return hr;
			}
		}
		
		hr = pITask->CreateTrigger ( &wTrigNumber, &pITaskTrig );
		
		if ( FAILED (hr) ) {
			TRACE1("CTask::SaveTask() - failed to create a task trigger. Return = 0x%08X\n", (DWORD) hr );
			return hr;
		}
		
		ZeroMemory ( &rTrigger, sizeof (TASK_TRIGGER) );
		
		rTrigger.cbTriggerSize = sizeof (TASK_TRIGGER);
		rTrigger.wBeginYear    = m_timeStart.wYear;
		rTrigger.wBeginMonth   = m_timeStart.wMonth;
		rTrigger.wBeginDay     = m_timeStart.wDay;
		rTrigger.wStartHour    = m_timeStart.wHour;
		rTrigger.wStartMinute  = m_timeStart.wMinute;
		
		if ( 0 != m_timeEnd.wYear ) {
			rTrigger.rgFlags   = TASK_TRIGGER_FLAG_HAS_END_DATE;
			rTrigger.wEndYear  = m_timeEnd.wYear;
			rTrigger.wEndMonth = m_timeEnd.wMonth;
			rTrigger.wEndDay   = m_timeEnd.wDay;
		}
		
		switch ( m_eFreq ) {
			case freqOnce: 
				rTrigger.TriggerType = TASK_TIME_TRIGGER_ONCE;
				break;
			case freqDaily:
				rTrigger.TriggerType = TASK_TIME_TRIGGER_DAILY;
				rTrigger.Type.Daily.DaysInterval = 1;
				break;
			case freqWeekly:
				rTrigger.TriggerType = TASK_TIME_TRIGGER_WEEKLY;
				rTrigger.Type.Weekly.rgfDaysOfTheWeek = GetDayOfWeekFlag ( m_timeStart );
				rTrigger.Type.Weekly.WeeksInterval = 1;
				break;
			case freqMonthly:
				rTrigger.TriggerType = TASK_TIME_TRIGGER_MONTHLYDATE;
				rTrigger.Type.MonthlyDate.rgfDays = 1 << ( m_timeStart.wDay - 1 );
				rTrigger.Type.MonthlyDate.rgfMonths = DDS_CST_EVERYMONTH;
				break;
			case freqAtLogon:
				rTrigger.TriggerType = TASK_EVENT_TRIGGER_AT_LOGON;
				break;
			DEFAULT_UNREACHABLE;
		}
		
		hr = pITaskTrig->SetTrigger ( &rTrigger );
		
		if ( FAILED(hr) ) {
			TRACE1("CTask::SaveTask() - failed to add trigger to the task. Return = 0x%08X\n", (DWORD) hr );
			return hr;
		}
		
		hr = pITask->QueryInterface ( IID_IPersistFile, (void **) &pIFile );
		
		if ( FAILED (hr) ) {
			TRACE1("CTask::SaveTask() - failed to get an IPersistFile interface on the task. Return = 0x%08X\n", (DWORD) hr );
			return hr;
		}
		
		hr = pIFile->Save ( NULL, FALSE );
		
		if ( FAILED(hr) ) {
			TRACE1("CTask::SaveTask() - error saving task. Return = 0x%08X\n", (DWORD) hr );
			return hr;
		}
	} __finally {
		if ( pIFile != NULL )
			pIFile->Release();
		
		if ( pITaskTrig != NULL )
			pITaskTrig->Release();
		
		if ( pITask != NULL )
			pITask->Release();
		
		if ( pISched != NULL )
			pISched->Release();
	}
	
	return hr;
}

HRESULT CTask::SaveTaskTS2( LPCTSTR szTaskName, BOOL bFailIfExists ) const
{
	HRESULT hr;

    ASSERT ( AfxIsValidString ( szTaskName ) );

    if ( 0 == lstrlen ( szTaskName ) ) {
		return E_FAIL;
	}

	CString m_sTimeStartTS2;
	CString m_sTimeEndTS2;

	if ( m_timeStart.wYear > 0 ) {
		m_sTimeStartTS2.Format(
			_T( "%04u-%02u-%02uT%02u:%02u:%02u" ),
			m_timeStart.wYear,
			m_timeStart.wMonth,
			m_timeStart.wDay,
			m_timeStart.wHour,
			m_timeStart.wMinute,
			m_timeStart.wSecond
		);
	}

	if ( m_timeEnd.wYear > 0 ) {
		m_sTimeEndTS2.Format(
			_T( "%04u-%02u-%02uT%02u:%02u:%02u" ),
			m_timeEnd.wYear,
			m_timeEnd.wMonth,
			m_timeEnd.wDay,
			m_timeEnd.wHour,
			m_timeEnd.wMinute,
			m_timeEnd.wSecond
		);
	}

	//  ------------------------------------------------------
    //  Create an instance of the Task Service.
	ITaskService *pService = NULL;
	hr = CoCreateInstance( CLSID_TaskScheduler, NULL, CLSCTX_INPROC_SERVER, IID_ITaskService, (void**)&pService );
	
	if ( FAILED(hr) ) {
		TRACE1( "Failed to create an instance of ITaskService: %x", hr );
		return hr;
	}
	
	//  Connect to the task service.
	hr = pService->Connect( _variant_t(), _variant_t(), _variant_t(), _variant_t() );
	
	if ( FAILED(hr) ) {
		TRACE1( "ITaskService::Connect failed: %x", hr );
		pService->Release();
		return hr;
	}

    //  ------------------------------------------------------
    //  Get the pointer to the root task folder.  This folder will hold the
    //  new task that is registered.
	ITaskFolder *pRootFolder = NULL;
	hr = pService->GetFolder( _bstr_t( L"\\") , &pRootFolder );
	
	if ( FAILED(hr) ) {
		TRACE1( "Cannot get Root Folder pointer: %x", hr );
		pService->Release();
		return hr;
	}
	
	//  If the same task exists, remove it.
	pRootFolder->DeleteTask( _bstr_t( szTaskName ), 0  );
	
	//  Create the task builder object to create the task.
	ITaskDefinition *pTask = NULL;
	hr = pService->NewTask( 0, &pTask );
	
	pService->Release();  // COM clean up.  Pointer is no longer used.
	
	if ( FAILED(hr) ) {
		TRACE1( "Failed to create a task definition: %x", hr );
		pRootFolder->Release();
		return hr;
	}
	
	//  ------------------------------------------------------
	//  Get the registration info for setting the identification.
	if ( m_sComment.GetLength() > 0 ) {
		IRegistrationInfo *pRegInfo = NULL;
		hr = pTask->get_RegistrationInfo( &pRegInfo );
		
		if ( FAILED(hr) ) {
			TRACE1( "\nCannot get identification pointer: %x", hr );
			pRootFolder->Release();
			pTask->Release();
			return hr;
		}
		
		hr = pRegInfo->put_Description( _bstr_t( m_sComment ) );
		pRegInfo->Release();
		
		if ( FAILED(hr) ) {
			TRACE1("\nCannot put identification info: %x", hr );
			pRootFolder->Release();
			pTask->Release();
			return hr;
		}
	}

	//  ------------------------------------------------------
	//  Create the principal for the task
	IPrincipal *pPrincipal = NULL;
	hr = pTask->get_Principal( &pPrincipal );

	if ( FAILED(hr) ) {
		TRACE1( "\nCannot get principal pointer: %x", hr );
		pRootFolder->Release();
		pTask->Release();
		return hr;
	}

	hr = pPrincipal->put_RunLevel( TASK_RUNLEVEL_HIGHEST );
	pPrincipal->Release();

	if ( FAILED(hr) ) {
		TRACE1( "\nCannot set run level: %x", hr );
		pRootFolder->Release();
		pTask->Release();
		return hr;
	}

	//  ------------------------------------------------------
	//  Create the settings for the task
	ITaskSettings *pSettings = NULL;
	hr = pTask->get_Settings( &pSettings );
	
	if ( FAILED(hr) ) {
		TRACE1( "\nCannot get settings pointer: %x", hr );
		pRootFolder->Release();
		pTask->Release();
		return hr;
	}
	
	// Set setting values for the task.
	// the execution time limit is infinite
	hr = pSettings->put_ExecutionTimeLimit( _bstr_t( L"PT0S" ) );
	
	if ( FAILED(hr) ) {
		TRACE1( "\nCannot put setting info: %x", hr );
		pSettings->Release();
		pRootFolder->Release();
		pTask->Release();
		return hr;
	}
	
	// THREAD_PRIORITY_LOWEST
	hr = pSettings->put_Priority(9);
	
	if ( FAILED(hr) ) {
		TRACE1( "\nCannot put setting info: %x", hr );
		pSettings->Release();
		pRootFolder->Release();
		pTask->Release();
		return hr;
	}

	hr = pSettings->put_DisallowStartIfOnBatteries( VARIANT_BOOL(false) );
	pSettings->Release();
	
	if ( FAILED(hr) ) {
		TRACE1( "\nCannot put setting info: %x", hr );
		pRootFolder->Release();
		pTask->Release();
		return hr;
	}
	
	//  ------------------------------------------------------
	//  Get the trigger collection to insert the trigger.
	ITriggerCollection *pTriggerCollection = NULL;
	hr = pTask->get_Triggers( &pTriggerCollection );
	
	if ( FAILED(hr) ) {
		TRACE1( "\nCannot get trigger collection: %x", hr );
		pRootFolder->Release();
		pTask->Release();
		return hr;
	}
	
	//  Add the trigger to the task.
	ITrigger *pTrigger = NULL;

	if ( m_eFreq == freqAtLogon ) {
		hr = pTriggerCollection->Create( TASK_TRIGGER_LOGON, &pTrigger );
	} else if ( m_eFreq == freqOnce ) {
		hr = pTriggerCollection->Create( TASK_TRIGGER_TIME, &pTrigger );
	} else if ( m_eFreq == freqDaily ) {
		hr = pTriggerCollection->Create( TASK_TRIGGER_DAILY, &pTrigger );
	} else if ( m_eFreq == freqWeekly ) {
		hr = pTriggerCollection->Create( TASK_TRIGGER_WEEKLY, &pTrigger );
	} else if ( m_eFreq == freqMonthly ) {
		hr = pTriggerCollection->Create( TASK_TRIGGER_MONTHLY, &pTrigger );
	} else { // unexpected
		TRACE1( "\nUnexpected ETaskFrequency: %x", m_eFreq );
		pTriggerCollection->Release();
		pRootFolder->Release();
		pTask->Release();
		return E_FAIL;
	}

	pTriggerCollection->Release();
	
	if ( FAILED(hr) ) {
		TRACE1( "\nCannot create the trigger: %x", hr );
		pRootFolder->Release();
		pTask->Release();
		return hr;
	}

	if ( m_eFreq == freqAtLogon ) {
		ILogonTrigger *pLogonTrigger = NULL;
		hr = pTrigger->QueryInterface( IID_ILogonTrigger, (void**) &pLogonTrigger );
		pTrigger->Release();
		
		if ( FAILED(hr) ) {
			TRACE1( "\nQueryInterface call failed for ILogonTrigger: %x", hr );
			pRootFolder->Release();
			pTask->Release();
			return hr;
		}
		
		//  Define the user. The task will execute when any user logs on.
		//  The specified user must be a user on this computer.
		hr = pLogonTrigger->put_UserId( NULL );
		
		if ( FAILED(hr) ) {
			TRACE1( "\nCannot add user ID to logon trigger: %x", hr );
			pLogonTrigger->Release();
			pRootFolder->Release();
			pTask->Release();
			return hr;
		}

		if ( m_sTimeStartTS2.GetLength() > 0 ) {
			hr = pLogonTrigger->put_StartBoundary( _bstr_t( m_sTimeStartTS2 ) );
			
			if ( FAILED(hr) ) {
				TRACE1( "\nCannot add start boundary to trigger: %x", hr );
				pLogonTrigger->Release();
				pRootFolder->Release();
				pTask->Release();
				return hr;
			}
		}

		if ( m_sTimeEndTS2.GetLength() > 0 ) {
			hr = pLogonTrigger->put_EndBoundary( _bstr_t( m_sTimeEndTS2 ) );
			
			if ( FAILED(hr) ) {
				TRACE1( "\nCannot add start boundary to trigger: %x", hr );
				pLogonTrigger->Release();
				pRootFolder->Release();
				pTask->Release();
				return hr;
			}
		}

		hr = pLogonTrigger->put_Delay( L"PT30S" );
		pLogonTrigger->Release();

		if ( FAILED(hr) ) {
			pRootFolder->Release();
			pTask->Release();
			return hr;
		}
	} else if ( m_eFreq == freqOnce ) {
		if ( m_sTimeStartTS2.GetLength() <= 0 ) {
			TRACE0( "\nInvalid StartBoundary" );
			pRootFolder->Release();
			pTask->Release();
			return E_FAIL;
		}

		ITimeTrigger *pTimeTrigger = NULL;
		hr = pTrigger->QueryInterface( IID_ITimeTrigger, (void**) &pTimeTrigger );
		pTrigger->Release();

		if ( FAILED(hr) ) {
			TRACE1( "\nQueryInterface call failed for ITimeTrigger: %x", hr );
			pRootFolder->Release();
			pTask->Release();
			return hr;
		}

		hr = pTimeTrigger->put_StartBoundary( _bstr_t( m_sTimeStartTS2 ) );
		
		if ( FAILED(hr) ) {
			TRACE1( "\nCannot add start boundary to trigger: %x", hr );
			pTimeTrigger->Release();
			pRootFolder->Release();
			pTask->Release();
			return hr;
		}

		if ( m_sTimeEndTS2.GetLength() > 0 ) {
			hr = pTimeTrigger->put_EndBoundary( _bstr_t( m_sTimeEndTS2 ) );
			
			if ( FAILED(hr) ) {
				TRACE1( "\nCannot add start boundary to trigger: %x", hr );
				pTimeTrigger->Release();
				pRootFolder->Release();
				pTask->Release();
				return hr;
			}
		}

		pTimeTrigger->Release();
	} else if ( m_eFreq == freqDaily ) {
		if ( m_sTimeStartTS2.GetLength() <= 0 ) {
			TRACE0( "\nInvalid StartBoundary" );
			pRootFolder->Release();
			pTask->Release();
			return E_FAIL;
		}

		IDailyTrigger *pDailyTrigger = NULL;
		hr = pTrigger->QueryInterface( IID_IDailyTrigger, (void**) &pDailyTrigger );
		pTrigger->Release();
		
		if ( FAILED(hr) ) {
			TRACE1( "\nQueryInterface call on IDailyTrigger failed: %x", hr );
			pRootFolder->Release();
			pTask->Release();
			return hr;
		}

		hr = pDailyTrigger->put_StartBoundary( _bstr_t( m_sTimeStartTS2 ) );

		if ( FAILED(hr) ) {
			TRACE1( "\nCannot put start boundary: %x", hr );
			pDailyTrigger->Release();
			pRootFolder->Release();
			pTask->Release();
			return hr;
		}
		
		if ( m_sTimeEndTS2.GetLength() > 0 ) {
			//  Set the time when the trigger is deactivated.
			hr = pDailyTrigger->put_EndBoundary( _bstr_t( m_sTimeEndTS2 ) );
			
			if ( FAILED(hr) ) {
				TRACE1( "\nCannot put the end boundary: %x", hr );
				pDailyTrigger->Release();
				pRootFolder->Release();
				pTask->Release();
				return hr;
			}
		}
		
		//  Define the interval for the daily trigger.
		hr = pDailyTrigger->put_DaysInterval( (short)1 );
		pDailyTrigger->Release();
		
		if ( FAILED(hr) ) {
			TRACE1( "\nCannot put days interval: %x", hr );
			pRootFolder->Release();
			pTask->Release();
			return hr;
		}
	} else if ( m_eFreq == freqWeekly ) {
		if ( m_sTimeStartTS2.GetLength() <= 0 ) {
			TRACE0( "\nInvalid StartBoundary" );
			pRootFolder->Release();
			pTask->Release();
			return E_FAIL;
		}

		IWeeklyTrigger *pWeeklyTrigger = NULL;
		hr = pTrigger->QueryInterface( IID_IWeeklyTrigger, (void**) &pWeeklyTrigger );
		pTrigger->Release();
		
		if ( FAILED(hr) ) {
			TRACE1( "\nQueryInterface call for IWeeklyTrigger failed: %x", hr );
			pRootFolder->Release();
			pTask->Release();
			return hr;
		}

		hr = pWeeklyTrigger->put_StartBoundary( _bstr_t( m_sTimeStartTS2 ) );
		
		if ( FAILED(hr) ) {
			TRACE1("\nCannot put the start boundary: %x", hr );
			pWeeklyTrigger->Release();
			pRootFolder->Release();
			pTask->Release();
			return hr;
		}

		if ( m_sTimeEndTS2.GetLength() > 0 ) {
			//  Set the time when the trigger is deactivated.
			hr = pWeeklyTrigger->put_EndBoundary( _bstr_t( m_sTimeEndTS2 ) );
			
			if ( FAILED(hr) ) {
				TRACE1( "\nCannot put the end boundary: %x", hr );
				pWeeklyTrigger->Release();
				pRootFolder->Release();
				pTask->Release();
				return hr;
			}
		}
		
		//  Define the interval for the weekly trigger.
		hr = pWeeklyTrigger->put_WeeksInterval( (short)1 );
		
		if ( FAILED(hr) ) {
			TRACE1("\nCannot put weeks interval: %x", hr );
			pWeeklyTrigger->Release();
			pRootFolder->Release();
			pTask->Release();
			return hr;
		}
		
		hr = pWeeklyTrigger->put_DaysOfWeek( (short) GetDayOfWeekFlag( m_timeStart ) );
		pWeeklyTrigger->Release();
		
		if ( FAILED(hr) ) {
			TRACE1( "\nCannot put days of week interval: %x", hr );
			pRootFolder->Release();
			pTask->Release();
			return hr;
		}
	} else if ( m_eFreq == freqMonthly ) {
		if ( m_sTimeStartTS2.GetLength() <= 0 ) {
			TRACE0( "\nInvalid StartBoundary" );
			pRootFolder->Release();
			pTask->Release();
			return E_FAIL;
		}

		IMonthlyTrigger *pMonthlyTrigger = NULL;
		hr = pTrigger->QueryInterface( IID_IMonthlyTrigger, (void**) &pMonthlyTrigger );
		pTrigger->Release();
		
		if ( FAILED(hr) ) {
			TRACE1( "\nQueryInterface call for IMonthlyTrigger failed: %x", hr );
			pRootFolder->Release();
			pTask->Release();
			return hr;
		}

		hr = pMonthlyTrigger->put_StartBoundary( _bstr_t( m_sTimeStartTS2 ) );
		
		if ( FAILED(hr) ) {
			TRACE1("\nCannot put the start boundary: %x", hr );
			pMonthlyTrigger->Release();
			pRootFolder->Release();
			pTask->Release();
			return hr;
		}

		if ( m_sTimeEndTS2.GetLength() > 0 ) {
			//  Set the time when the trigger is deactivated.
			hr = pMonthlyTrigger->put_EndBoundary( _bstr_t( m_sTimeEndTS2 ) );
			
			if ( FAILED(hr) ) {
				TRACE1( "\nCannot put the end boundary: %x", hr );
				pMonthlyTrigger->Release();
				pRootFolder->Release();
				pTask->Release();
				return hr;
			}
		}
		
		hr = pMonthlyTrigger->put_DaysOfMonth( (long) 1 << ( m_timeStart.wDay - 1 ) );
		pMonthlyTrigger->Release();
		
		if ( FAILED(hr) ) {
			TRACE1( "\nCannot put days of month interval: %x", hr );
			pRootFolder->Release();
			pTask->Release();
			return hr;
		}
	} else { // unexpected
		TRACE1( "\nUnexpected ETaskFrequency: %x", m_eFreq );
		pRootFolder->Release();
		pTask->Release();
		return E_FAIL;
	}

    //  ------------------------------------------------------
    //  Add an Action to the task. This task will execute notepad.exe.
    IActionCollection *pActionCollection = NULL;
    
    //  Get the task action collection pointer.
    hr = pTask->get_Actions( &pActionCollection );
    
    if ( FAILED(hr) ) {
    	TRACE1( "\nCannot get Task collection pointer: %x", hr );
    	pRootFolder->Release();
    	pTask->Release();
    	return hr;
    }
    
    //  Create the action, specifying that it is an executable action.
    IAction *pAction = NULL;
    hr = pActionCollection->Create( TASK_ACTION_EXEC, &pAction );
    pActionCollection->Release();
    
    if ( FAILED(hr) ) {
    	TRACE1( "\nCannot create the action: %x", hr );
    	pRootFolder->Release();
    	pTask->Release();
    	return hr;
    }
    
    IExecAction *pExecAction = NULL;
    //  QI for the executable task pointer.
    hr = pAction->QueryInterface( IID_IExecAction, (void**) &pExecAction );
    pAction->Release();
    
    if ( FAILED(hr) ) {
    	TRACE1( "\nQueryInterface call failed for IExecAction: %x", hr );
    	pRootFolder->Release();
    	pTask->Release();
    	return hr;
    }
    
    //  Set the path of the executable
    hr = pExecAction->put_Path( _bstr_t( m_sProgramPath ) );
    
    if ( FAILED(hr) ) {
    	TRACE1( "\nCannot set path of executable: %x", hr );
    	pExecAction->Release();
    	pRootFolder->Release();
    	pTask->Release();
    	return hr;
    }

	if ( m_sParameters.GetLength() > 0 ) {
		hr = pExecAction->put_Arguments( _bstr_t( m_sParameters ) );
		
		if ( FAILED(hr) ) {
			TRACE1( "\nCannot set path of executable: %x", hr );
			pExecAction->Release();
			pRootFolder->Release();
			pTask->Release();
			return hr;
		}
	}
	
	if ( m_sStartingDir.GetLength() > 0 ) {
		hr = pExecAction->put_WorkingDirectory( _bstr_t( m_sStartingDir ) );
		
		if ( FAILED(hr) ) {
			TRACE1( "\nCannot set path of executable: %x", hr );
			pExecAction->Release();
			pRootFolder->Release();
			pTask->Release();
			return hr;
		}
	}

	pExecAction->Release();
	
	//  ------------------------------------------------------
    //  Save the task in the root folder.
    IRegisteredTask *pRegisteredTask = NULL;

	if ( m_eFreq == freqAtLogon ) {
		hr = pRootFolder->RegisterTaskDefinition(
			_bstr_t( szTaskName ),
			pTask,
			TASK_CREATE_OR_UPDATE,
			_variant_t( L"Builtin\\Administrators" ),
			_variant_t(),
			TASK_LOGON_GROUP,
			_variant_t( L"" ),
			&pRegisteredTask
		);
	} else if ( m_eFreq == freqOnce ) {
		if ( 0 == lstrlen ( m_sAccount ) ) {
			hr = pRootFolder->RegisterTaskDefinition(
				_bstr_t( szTaskName ),
				pTask,
				TASK_CREATE_OR_UPDATE,
				_variant_t( L"SYSTEM" ),
				_variant_t(),
				TASK_LOGON_SERVICE_ACCOUNT,
				_variant_t( L"" ),
				&pRegisteredTask
			);
		} else {
			hr = pRootFolder->RegisterTaskDefinition(
				_bstr_t( szTaskName ),
				pTask,
				TASK_CREATE_OR_UPDATE,
				_variant_t(),
				_variant_t(),
				TASK_LOGON_INTERACTIVE_TOKEN,
				_variant_t( L"" ),
				&pRegisteredTask
			);
		}
	} else if ( m_eFreq == freqDaily || m_eFreq == freqWeekly || m_eFreq == freqMonthly ) {
		if ( 0 == lstrlen ( m_sAccount ) ) {
			hr = pRootFolder->RegisterTaskDefinition(
				_bstr_t( szTaskName ),
				pTask,
				TASK_CREATE_OR_UPDATE,
				_variant_t( L"SYSTEM" ),
				_variant_t(),
				TASK_LOGON_SERVICE_ACCOUNT,
				_variant_t( L"" ),
				&pRegisteredTask
			);
		} else {
			hr = pRootFolder->RegisterTaskDefinition(
				_bstr_t( szTaskName ),
				pTask,
				TASK_CREATE_OR_UPDATE,
				_variant_t( _bstr_t( m_sAccount ) ),
				_variant_t( _bstr_t( m_sPassword ) ),
				TASK_LOGON_PASSWORD,
				_variant_t( L"" ),
				&pRegisteredTask
			);
		}
	} else { // unexpected
		TRACE1( "\nUnexpected ETaskFrequency: %x", m_eFreq );
		pRootFolder->Release();
		pTask->Release();
		return E_FAIL;
	}
    
    if ( FAILED(hr) ) {
    	TRACE1( "\nError saving the Task : %x", hr );
    	pRootFolder->Release();
    	pTask->Release();
    	return hr;
    }
    
    // Clean up
    pRootFolder->Release();
    pTask->Release();
    pRegisteredTask->Release();
    
    return hr;
}

HRESULT CTask::DeleteTask ( LPCTSTR szTaskName )
{
	if ( GetVersionTS() == TRUE ) {
		return DeleteTaskTS2( szTaskName );
	} else {
		return DeleteTaskTS1( szTaskName );
	}
}

HRESULT CTask::DeleteTaskTS1 ( LPCTSTR szTaskName )
{
	HRESULT         hr;
	ITaskScheduler* pISched = NULL;

    USES_CONVERSION;

    ASSERT ( AfxIsValidString ( szTaskName ) );

    if ( 0 == lstrlen ( szTaskName ) ) {
		return E_FAIL;
	}
	
	hr = ::CoCreateInstance (
		CLSID_CTaskScheduler,
		NULL,
		CLSCTX_INPROC_SERVER,
		IID_ITaskScheduler,
		(void **) &pISched
	);
	
	if ( FAILED(hr) ) {
#ifdef _DEBUG
        TRACE1("CTask::DeleteTask() - failed to create a task scheduler interface. Return = 0x%08X\n",
               (DWORD) hr );

        if ( REGDB_E_CLASSNOTREG == hr )
            {
            TRACE0("    The error was REGDB_E_CLASSNOTREG, meaning you don't have the scheduler installed.\n"
                _T("    If you are running 95 or NT 4 with IE 4, you must install the task scheduler from the\n")
                _T("    IE components install page on MS's web site or the IE CD.\n")
                _T("    If you're on 98, NT 5, or 95/NT 4 with IE 5, then something's wrong with your install\n")
                _T("    because the scheduler should always be present.\n")
                _T("    Note that this class does *NOT* work with the \"AT\" service, which is the default\n")
                _T("    scheduler on NT 4 and earlier.\n") );
            }
#endif  // _DEBUG
		return hr;
	}
	
	hr = pISched->Delete ( T2COLE ( szTaskName ) );
	
	if ( FAILED(hr) ) {
        TRACE1("CTask::DeleteTask() - failed to delete task. Return = 0x%08X\n", (DWORD) hr );
	}
	
	pISched->Release();

    return hr;
}

HRESULT CTask::DeleteTaskTS2 ( LPCTSTR szTaskName )
{
	HRESULT hr;

    ASSERT ( AfxIsValidString ( szTaskName ) );

    if ( 0 == lstrlen ( szTaskName ) ) {
		return E_FAIL;
	}

	//  ------------------------------------------------------
    //  Create an instance of the Task Service.
	ITaskService *pService = NULL;

	hr = ::CoCreateInstance (
		CLSID_TaskScheduler,
		NULL,
		CLSCTX_INPROC_SERVER,
		IID_ITaskService,
		(void**)&pService
	);
	
	if ( FAILED(hr) ) {
		TRACE1( "Failed to create an instance of ITaskService: %x", hr );
		return hr;
	}
	
	//  Connect to the task service.
	hr = pService->Connect( _variant_t(), _variant_t(), _variant_t(), _variant_t() );
	
	if ( FAILED(hr) ) {
		TRACE1( "ITaskService::Connect failed: %x", hr );
		pService->Release();
		return hr;
	}

    //  ------------------------------------------------------
    //  Get the pointer to the root task folder.
	ITaskFolder *pRootFolder = NULL;
	hr = pService->GetFolder( _bstr_t( L"\\") , &pRootFolder );
	pService->Release();
	
	if ( FAILED(hr) ) {
		TRACE1( "Cannot get Root Folder pointer: %x", hr );
		pService->Release();
		return hr;
	}
	
	//  If the same task exists, remove it.
	hr = pRootFolder->DeleteTask( _bstr_t( szTaskName ), 0 );
	pRootFolder->Release();

	if ( FAILED(hr) ) {
		TRACE1( "Cannot delete task: %x", hr );
	}

    return hr;
}

HRESULT CTask::TaskExisted ( LPCTSTR szTaskName, BOOL& isFound )
{
	if ( GetVersionTS() == TRUE ) {
		return TaskExistedTS2( szTaskName, isFound );
	} else {
		return TaskExistedTS1( szTaskName, isFound );
	}
}


HRESULT CTask::TaskExisted2 ( LPCTSTR szTaskName, BOOL& isFound )
{
	if ( GetVersionTS() == TRUE ) {
		return TaskExistedTS2( szTaskName, isFound );
	} else {
		return TaskExistedTS1( szTaskName, isFound );
	}
}
HRESULT CTask::TaskExistedTS1 ( LPCTSTR szTaskName, BOOL& isFound )
{
	HRESULT         hr;
	ITaskScheduler* pISched = NULL;
	IUnknown*       pIUnk = NULL;
	isFound = FALSE;

    USES_CONVERSION;

    ASSERT ( AfxIsValidString ( szTaskName ) );

    if ( 0 == lstrlen ( szTaskName ) ) {
		return E_FAIL;
	}
	
	hr = ::CoCreateInstance (
		CLSID_CTaskScheduler,
		NULL,
		CLSCTX_INPROC_SERVER,
		IID_ITaskScheduler,
		(void **) &pISched
	);

	if ( FAILED(hr) ) {
		return hr;
	}

	hr = pISched->Activate ( T2COLE ( szTaskName ), IID_ITask, &pIUnk );

	if ( SUCCEEDED(hr) ) {
		isFound = TRUE;
		pIUnk->Release();
		pIUnk = NULL;
	} else {
		isFound = FALSE;
	}

	pISched->Release();
	
	return hr;
}

HRESULT CTask::TaskExistedTS2 ( LPCTSTR szTaskName, BOOL& isFound )
{
	HRESULT hr;

    ASSERT ( AfxIsValidString ( szTaskName ) );

    if ( 0 == lstrlen ( szTaskName ) ) {
		return E_FAIL;
	}

	//  ------------------------------------------------------
    //  Create an instance of the Task Service.
	ITaskService *pService = NULL;

	hr = ::CoCreateInstance (
		CLSID_TaskScheduler,
		NULL,
		CLSCTX_INPROC_SERVER,
		IID_ITaskService,
		(void**)&pService
	);
	
	if ( FAILED(hr) ) {
		TRACE1( "Failed to create an instance of ITaskService: %x", hr );
		return hr;
	}
	
	//  Connect to the task service.
	hr = pService->Connect( _variant_t(), _variant_t(), _variant_t(), _variant_t() );
	
	if ( FAILED(hr) ) {
		TRACE1( "ITaskService::Connect failed: %x", hr );
		pService->Release();
		return hr;
	}

    //  ------------------------------------------------------
    //  Get the pointer to the root task folder.  This folder will hold the
    //  new task that is registered.
	ITaskFolder *pRootFolder = NULL;
	hr = pService->GetFolder( _bstr_t( L"\\") , &pRootFolder );
	
	if ( FAILED(hr) ) {
		TRACE1( "Cannot get Root Folder pointer: %x", hr );
		pService->Release();
		return hr;
	}

	IRegisteredTask *pRegisteredTask = NULL;

	hr = pRootFolder->GetTask( _bstr_t( szTaskName ), &pRegisteredTask );
	pRootFolder->Release();

	if ( SUCCEEDED(hr) ) {
		isFound = TRUE;
		pRegisteredTask->Release();
	} else {
		isFound = FALSE;
	}

    return hr;
}

WORD CTask::GetDayOfWeekFlag ( const SYSTEMTIME& s_time ) const
{
	static WORD s_wDayFlags[] = {
		0, TASK_SUNDAY, TASK_MONDAY, TASK_TUESDAY,
		TASK_WEDNESDAY, TASK_THURSDAY, TASK_FRIDAY,
		TASK_SATURDAY
	};

	CTime time( s_time );
	
	WORD wRet = 0;
	int nDayOfWeek = time.GetDayOfWeek();

    ASSERT ( nDayOfWeek >= 1 && nDayOfWeek <= 7 );
    wRet = s_wDayFlags [ nDayOfWeek ];
    ASSERT ( wRet != 0 );

    return wRet;
}